package spring.cloud.tasks.tasks_manager.schedule;

import lombok.extern.slf4j.Slf4j;
import spring.cloud.tasks.common.curator.Curator;
import spring.cloud.tasks.tasks_manager.dto.TaskDto;
import spring.cloud.tasks.tasks_manager.service.TaskInstanceService;
import spring.cloud.tasks.tasks_manager.taskInstanceManager.BatchTaskTaskInstanceManager;
import spring.cloud.tasks.tasks_manager.taskInstanceManager.StateFullSimpleTaskTaskInstanceManager;
import spring.cloud.tasks.tasks_manager.taskInstanceManager.StateLessSimpleTaskTaskInstanceManager;
import spring.cloud.tasks.tasks_manager.taskInstanceManager.TaskInstanceManager;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
//TaskIdToTaskConfigurationMap taskIdToTaskConfigurationMap = scheduleManager.taskConfigurationManager.taskIdToTaskConfigurationMap;
//需要删除/新增 实例信息。
//针对简单分片的情况。
//这里需要提到避免一个情况，立即禁用，然后立即启用。这个情况。会造成任务实例信息的错乱以及难以想象的问题。这些问题暂时我也不知道。
//理想的是将状态分为短期禁用 长期禁用 启用。这三个状态。
//如果长期禁用则代表需要将实例信息从内存清理掉，数据库任务实例信息取消。如果短期禁用，暂时内存保留实例信息，数据库信息标记为暂时取消（暂停任务，但不确定以后是否该任务实例信息会被执行），如果超过15分钟，还没有进行启用，那么也需要把内存和数据库的任务实例信息取消。这样可以避免很多问题。
//整体解决办法:测试环境和开发环境可能会出现经常禁用和启用的情况，生产上不会频繁禁用和启用。故只需要禁用后立即让以前的未执行的实例信息取消即可。如果在启用那么新建任务实例信息即可。
//另外一种方案:先将失效的任务标记为禁用。如果启用的时候，需要进行调度，如果时间相关的签名没有改变，且没有过时间点，那么可用加载到内存里面。否则需要立即重新生成。
//如果是批次任务，还需要考虑其他的。
//如果存在正在执行的任务需要立即取消。包括两个方面。1立即停止任务。2从预分配里面删除。
//还没有进预分配队列的 进了预分配队列但是没有到执行时间的，到了执行时间点需要立即停止还是等待执行完毕。。。

/**
 * 只要任务不主动禁用或者删除。那么默认的任务会正常的注册。不会出现任务实例被取消和清理的情况。那么程序可以进行状态维护。
 */
@Slf4j
public class TaskInstanceRegisterManager {
    protected ScheduleManager scheduleManager;
    private final String nameSpace;
    protected Curator curator;
    private TaskInstanceService taskInstanceService;

    public TaskInstanceRegisterManager(ScheduleManager scheduleManager, String nameSpace, Curator curator) {
        this.scheduleManager = scheduleManager;
        this.nameSpace = nameSpace;
        this.curator = curator;
        //
        this.taskInstanceService = scheduleManager.getTaskInstanceService();
    }

    TaskTaskInstanceManagerContext taskTaskInstanceManagerContext = new TaskTaskInstanceManagerContext();

    //创建时间点的线程和生成实例的线程不是一个线程。对象实时刻刻都在被改变。那么需要copy一份。
    public void refresh() throws Exception {
        /**
         * 这个信息是当前某个时间点的快照信息.这里是创建任务待执行的任务实例。
         */
        TaskFireDateTimeAndPreAssignDateTimeManager.Context $context$ = scheduleManager.taskFireDateTimeAndPreAssignDateTimeManager.context;//串行执行
        //引用操作
        List<String> $taskIdList$ = $context$.taskIdList;
        List<String> $enableTaskIdList$ = $context$.enableTaskIdList;
        List<String> $pauseTaskIdList$ = $context$.pauseTaskIdList;
        ConcurrentHashMap<String, TaskFireDateTimeAndPreAssignDateTimeListHolder> taskIdToTaskFireDateTimeAndPreAssignDateTimeListHolderMap = $context$.taskIdToFireDateTimeAndPreAssignDateTimeListContextMap;
        Set<String> $taskIdSet$ = taskIdToTaskFireDateTimeAndPreAssignDateTimeListHolderMap.keySet();
        //
        TaskConfigurationManager taskConfigurationManager = scheduleManager.taskConfigurationManager;
        ConcurrentHashMap<String, TaskConfiguration> taskIdToTaskConfigurationMap = taskConfigurationManager.taskIdToTaskConfigurationMap;
        ConcurrentHashMap<String, SlightlyAdjustTaskInstances> taskIdToSlightlyAdjustTaskInstancesMap = new ConcurrentHashMap<>();
        List<SlightlyAdjustTaskInstances> slightlyAdjustTaskInstancesList = new ArrayList<>();
        //针对异常编程，建立参照物，可数据恢复
        List<SlightlyAdjustTaskInstances.BackupInformation> slightlyAdjustTaskInstancesBackupInformationList = new ArrayList<>();
        Date nowDate = new Date();
        for (String $taskId$ : $taskIdSet$) {
            //
            TaskConfiguration taskConfiguration = taskIdToTaskConfigurationMap.get($taskId$);
            if (taskConfiguration == null) {
                continue;
            }

            TaskDto taskDto = taskConfiguration.getTaskDto();
            String taskType = taskDto.getTaskType();
            String stateFullTask = taskDto.getStateFullTask();
            String taskId = taskDto.getTaskId();
            long updateVersion = taskDto.getUpdateVersion();//保证这是最新的(定时获取最新的+watch机制)
            //
            SlightlyAdjustTaskInstances slightlyAdjustTaskInstances = new SlightlyAdjustTaskInstances();
            slightlyAdjustTaskInstances.setTaskId(taskId);
            slightlyAdjustTaskInstances.setTaskType(taskType);
            slightlyAdjustTaskInstances.setStateFullTask(stateFullTask);
            slightlyAdjustTaskInstances.setUpdateVersion(updateVersion);
            taskIdToSlightlyAdjustTaskInstancesMap.put(taskId, slightlyAdjustTaskInstances);
            slightlyAdjustTaskInstancesList.add(slightlyAdjustTaskInstances);
            //
            TaskInstanceManager taskInstanceManager = TaskInstanceRegisterManagerUtil.getTaskInstanceManager(scheduleManager, taskTaskInstanceManagerContext, taskType, stateFullTask);
            TaskFireDateTimeAndPreAssignDateTimeListHolder holder = taskIdToTaskFireDateTimeAndPreAssignDateTimeListHolderMap.get($taskId$);
            //
            Long updateVersionValue = taskInstanceManager.taskIdToUpdateVersionMap.get(taskId);
            CopyOnWriteArrayList<TaskInstanceInfo> taskInstanceInfoList = taskInstanceManager.get(taskId, updateVersion);
            if (updateVersionValue == null) {
                //当前的最新版本就是自己
                List<FireDateTimeAndPreAssignDateTime> fireDateTimeAndPreAssignDateTimeList = holder.getFireDateTimeAndPreAssignDateTimeList();
                for (FireDateTimeAndPreAssignDateTime fireDateTimeAndPreAssignDateTime : fireDateTimeAndPreAssignDateTimeList) {
                    Date nextPreAssignDate = fireDateTimeAndPreAssignDateTime.getNextPreAssignDate();
                    Date nextFireDate = fireDateTimeAndPreAssignDateTime.getNextFireDate();
                    //一分钟以前的就不执行了
                    if (nextFireDate.before(nowDate)) {//这里可能造成任务丢失
                        continue;
                    } else {
                        TaskInstanceInfo taskInstanceInfo = TaskInstanceRegisterManagerUtil.buildTaskInstanceInfo(taskDto, fireDateTimeAndPreAssignDateTime);
                        taskInstanceInfoList.add(taskInstanceInfo);
                        slightlyAdjustTaskInstances.getAdd().add(taskInstanceInfo);
                        //

                    }
                }
            } else {
                //这里获取到的版本是当前最新的，不可能出现版本逆退问题
                if (updateVersionValue.longValue() == updateVersion) {
                    //版本相同
                    List<FireDateTimeAndPreAssignDateTime> fireDateTimeAndPreAssignDateTimeList = holder.getFireDateTimeAndPreAssignDateTimeList();
                    for (FireDateTimeAndPreAssignDateTime fireDateTimeAndPreAssignDateTime : fireDateTimeAndPreAssignDateTimeList) {
                        Date nextPreAssignDate = fireDateTimeAndPreAssignDateTime.getNextPreAssignDate();
                        Date nextFireDate = fireDateTimeAndPreAssignDateTime.getNextFireDate();
                        if (nextFireDate.before(nowDate)) {//只添加还没有到执行点的任务
                            continue;
                        } else {
                            //开始创建任务实例信息。这里需要考虑正在执行的任务。
                            boolean hasCreated = false;
                            for (TaskInstanceInfo taskInstanceInfo : taskInstanceInfoList) {
                                if (taskInstanceInfo.getNextPreAssignDate().getTime() == nextPreAssignDate.getTime() &&//????暂时这样吧。
                                        taskInstanceInfo.getNextFireDate().getTime() == nextFireDate.getTime()) {
                                    hasCreated = true;
                                    continue;//直接忽略
                                }
                            }
                            if (!hasCreated) {
                                //创建任务实例信息。
                                TaskInstanceInfo taskInstanceInfo = TaskInstanceRegisterManagerUtil.buildTaskInstanceInfo(taskDto, fireDateTimeAndPreAssignDateTime);
                                taskInstanceInfoList.add(taskInstanceInfo);
                                slightlyAdjustTaskInstances.getAdd().add(taskInstanceInfo);
                                continue;
                            }
                        }
                    }
                } else {
                    //任务存在版本更迭
                    //需要对老的版本的任务实例，还没有进行执行的任务进行取消（包括已经进行预调度了的）
                    slightlyAdjustTaskInstances.setDeleteOldVersionTaskInstances(true);
                    //然后对当前版本进行新增，如果需要立即执行的则放入立即分配队列，其他情况放入【滑动时间任务实例队列】里面
                    List<FireDateTimeAndPreAssignDateTime> fireDateTimeAndPreAssignDateTimeList = holder.getFireDateTimeAndPreAssignDateTimeList();
                    for (FireDateTimeAndPreAssignDateTime fireDateTimeAndPreAssignDateTime : fireDateTimeAndPreAssignDateTimeList) {
                        Date nextPreAssignDate = fireDateTimeAndPreAssignDateTime.getNextPreAssignDate();
                        Date nextFireDate = fireDateTimeAndPreAssignDateTime.getNextFireDate();
                        //一分钟以前的就不执行了
                        if (nextFireDate.before(nowDate)) {//这里可能造成任务丢失
                            continue;
                        } else {
                            TaskInstanceInfo taskInstanceInfo = TaskInstanceRegisterManagerUtil.buildTaskInstanceInfo(taskDto, fireDateTimeAndPreAssignDateTime);
                            taskInstanceInfoList.add(taskInstanceInfo);
                            slightlyAdjustTaskInstances.getAdd().add(taskInstanceInfo);
                        }
                    }
                }
            }
        }
        for (SlightlyAdjustTaskInstances slightlyAdjustTaskInstances : slightlyAdjustTaskInstancesList) {
            SlightlyAdjustTaskInstances.BackupInformation backupInformation = slightlyAdjustTaskInstances.backUp();
            slightlyAdjustTaskInstancesBackupInformationList.add(backupInformation);
        }
        /**
         * 完全新建的
         */
        List<TaskInstanceInfo> addTaskInstanceInfoList = new ArrayList<>();
        for (SlightlyAdjustTaskInstances adjustSlightlyTaskInstances : slightlyAdjustTaskInstancesList) {
            addTaskInstanceInfoList.addAll(adjustSlightlyTaskInstances.getAdd());
        }
        //
        /**
         * 整个框架假定任务实例保存不会出问题。如果这步操作都出现了问题，那么代表整个系统的数据库都有问题了。这由运维保证。
         */
        boolean batchAdd = taskInstanceService.batchAdd(addTaskInstanceInfoList);
        if (!batchAdd) {
            //等待下次机会进行实例信息保存成功
            return;
        }
        ConcurrentHashMap<String, TaskInstanceInfo> unManagedTaskIdAndTaskInstanceIdToTaskInstanceInfo = scheduleManager.unManagedTaskIdAndTaskInstanceIdToTaskInstanceInfo;
        //

        for (TaskInstanceInfo taskInstanceInfo : addTaskInstanceInfoList) {
            String taskId = taskInstanceInfo.getTaskId();
            String taskInstanceId = taskInstanceInfo.getTaskInstanceId();
            //
            TaskInstanceManager taskInstanceManager = TaskInstanceRegisterManagerUtil.getTaskInstanceManager(scheduleManager, taskTaskInstanceManagerContext, taskInstanceInfo.getTaskType(), taskInstanceInfo.getStateFullTask());
            CopyOnWriteArrayList<TaskInstanceInfo> taskInstanceInfoList = taskInstanceManager.get(taskId, taskInstanceInfo.getVersion());
            taskInstanceInfoList.add(taskInstanceInfo);
            //这个时候还没有进行添加到任务管理容器中。
            unManagedTaskIdAndTaskInstanceIdToTaskInstanceInfo.put(taskId + "-" + taskInstanceId, taskInstanceInfo);
        }
        //
        int maxTryTime = 3;
        int tryTime = 0;
        boolean ok = false;
        while (true) {
            tryTime++;
            ok = taskInstanceInfoRegisterAtZk(addTaskInstanceInfoList, taskIdToTaskConfigurationMap);
            if (ok) {
                break;
            } else {
                if (tryTime > maxTryTime) {
                    break;
                }
            }
        }
        if (!ok) {
            //立即告警
            return;
        }
        //注册成功后，先需要将老的实例取消，新的实例信息设置为可以预调度。
        List<SlightlyAdjustTaskInstances> $slightlyAdjustTaskInstancesList$ = new ArrayList<>();
        for (SlightlyAdjustTaskInstances slightlyAdjustTaskInstances : slightlyAdjustTaskInstancesList) {
            boolean deleteOldVersionTaskInstances = slightlyAdjustTaskInstances.deleteOldVersionTaskInstances;
            if (deleteOldVersionTaskInstances) {
                //需要立即停止之前的实例（内存先）
                $slightlyAdjustTaskInstancesList$.add(slightlyAdjustTaskInstances);
            }
        }
        /**
         * 版本不对的实例可以不用执行了。
         */
        for (SlightlyAdjustTaskInstances slightlyAdjustTaskInstances : $slightlyAdjustTaskInstancesList$) {
            String taskId = slightlyAdjustTaskInstances.taskId;
            String taskType = slightlyAdjustTaskInstances.taskType;
            String stateFullTask = slightlyAdjustTaskInstances.stateFullTask;
            long updateVersion = slightlyAdjustTaskInstances.updateVersion;
            TaskInstanceManager taskInstanceManager = TaskInstanceRegisterManagerUtil.getTaskInstanceManager(scheduleManager, taskTaskInstanceManagerContext, taskType, stateFullTask);
            taskInstanceManager.tryCancel(taskId, updateVersion);
        }
        for (TaskInstanceInfo taskInstanceInfo : addTaskInstanceInfoList) {
            taskInstanceInfo.setSchedule(true);
        }
        scheduleManager.slidingTimeTaskBucketQueueService.scheduleTask(addTaskInstanceInfoList);
        //
        //注册成功后，需要将待取消的任务实例立即取消
        //
        //需要对历史的任务实例信息进行清理。要不然就会造成内存移除。
        //如果历史的实例信息没有注册成功（指的是实例信息注册+分片信息注册+实例信息修改为分片信息注册成功），那么最多保存120分钟即可。
        //这样给与2个小时的时候进行处理。但是这样是会存在问题的。如果调度器挂了，再新启动一个调度器，只会处理历史已经提交的任务实例。
        //同时对还没有生成实例的执行计划生成实例。
        // （这里需要进行判断的是否是首次实例注册，如果是首次实例注册则需要拉取ZK的实例信息对比。比较出差异，然后进行增量处理（删除 新增））
    }

    public static class TaskTaskInstanceManagerContext {
        public StateFullSimpleTaskTaskInstanceManager stateFullSimpleTaskTaskInstanceManager;
        public StateLessSimpleTaskTaskInstanceManager stateLessSimpleTaskTaskInstanceManager;
        public BatchTaskTaskInstanceManager batchTaskTaskInstanceManager;
    }


    /**
     * ZK任务实例信息注册
     */
    private boolean taskInstanceInfoRegisterAtZk(//
                                                 List<TaskInstanceInfo> addTaskInstanceInfoList, Map<String, TaskConfiguration> taskIdToTaskConfigurationMap
    ) throws Exception {//重跑的任务实例
        //新添加的任务实例
        for (TaskInstanceInfo taskInstanceInfo : addTaskInstanceInfoList) {
            TaskConfiguration taskConfiguration = taskIdToTaskConfigurationMap.get(taskInstanceInfo.getTaskId());
            if (taskConfiguration == null) {
                continue;
            }

            TaskDto taskDto = taskConfiguration.getTaskDto();
            TaskTouchInfo.Context.put(taskInstanceInfo.getTaskId());
            Boolean ok = TaskInstanceRegisterManagerUtil.taskInstanceInfoRegisterAtZkAndCreateTaskItemsAtZk(scheduleManager, curator, taskInstanceInfo, taskDto);
            if (ok) {
            } else {
                log.error("");
            }
        }
        return true;
    }

    //这几个需要设置为预调度
    public Boolean rerun(ReRunTaskInstance reRunTaskInstance) throws Exception {
        TaskDto taskDto = scheduleManager.taskConfigurationManager.getTaskConfiguration(reRunTaskInstance.getTaskId()).getTaskDto();
        if (taskDto == null) {
            return null;
        }
        TaskInstanceInfo taskInstanceInfo = TaskInstanceRegisterManagerUtil.buildReRunTaskInstanceInfo(taskDto, reRunTaskInstance);
        TaskTouchInfo.Context.put(taskInstanceInfo.getTaskId());
        return TaskInstanceRegisterManagerUtil.taskInstanceInfoRegisterAtZkAndCreateTaskItemsAtZk(scheduleManager, curator, taskInstanceInfo, taskDto);

    }

    public Boolean rightNowRun(RightNowRunTaskInstance rightNowRunTaskInstance) throws Exception {
        TaskDto taskDto = scheduleManager.taskConfigurationManager.getTaskConfiguration(rightNowRunTaskInstance.getTaskId()).getTaskDto();
        if (taskDto == null) {
            return null;
        }
        TaskInstanceInfo taskInstanceInfo = TaskInstanceRegisterManagerUtil.buildRightNowRunTaskInstanceInfo(taskDto, rightNowRunTaskInstance);
        TaskTouchInfo.Context.put(taskInstanceInfo.getTaskId());
        boolean ok = TaskInstanceRegisterManagerUtil.taskInstanceInfoRegisterAtZkAndCreateTaskItemsAtZk(scheduleManager, curator, taskInstanceInfo, taskDto);
        if (ok) {
            TaskInstanceRegisterManagerUtil.addWaittingAssignedTask(scheduleManager, taskInstanceInfo);
        }
        return ok;
    }

    /**
     * 这里涉及到一个资源评估的功能。
     */
    public Boolean rightNowRun(UserSubmitRequestTaskInstance userSubmitRequestTaskInstance) throws Exception {
        TaskDto taskDto = scheduleManager.taskConfigurationManager.getTaskConfiguration(userSubmitRequestTaskInstance.getTaskId()).getTaskDto();
        if (taskDto == null) {
            return null;
        }
        TaskInstanceInfo taskInstanceInfo = TaskInstanceRegisterManagerUtil.buildUserSubmitRequestTaskInstanceInfo(taskDto, userSubmitRequestTaskInstance);
        TaskTouchInfo.Context.put(taskInstanceInfo.getTaskId());
        boolean ok = TaskInstanceRegisterManagerUtil.taskInstanceInfoRegisterAtZkAndCreateTaskItemsAtZk(scheduleManager, curator, taskInstanceInfo, taskDto);
        if (ok) {
            TaskInstanceRegisterManagerUtil.addWaittingAssignedTask(scheduleManager, taskInstanceInfo);
        }
        return ok;
    }
}

